//
//  CWPlayerView.swift
//  SwiftTest
//
//  Created by 陈旺 on 2022/2/8.
//

import UIKit
import AVFoundation

//MARK: - 播放器视图
class CWPlayerView: UIView {

    /// layer
    override class var layerClass: AnyClass {
        return AVPlayerLayer.classForCoder()
    }

    /// 播放器
    var player: AVPlayer?

    /// 当前播放item
    var currentItem: AVPlayerItem?

    /// 当前播放地址
    var currentUrlStr: String?

    /// player 监听回调
    fileprivate var playClockTimeObserver: Any?

    /// 准备好后是否自动播放
    fileprivate var autoPlay: Bool = false

    /// 是否在播放中
    var isPlaying: Bool {
        if let player = self.player {
            return player.timeControlStatus == .playing
        }
        return false
    }

    /// 菊花
    private lazy var activityView: UIActivityIndicatorView = {
        let target = UIActivityIndicatorView.init(style: .large)
        target.hidesWhenStopped = true
        target.color = .white
        return target
    }()

    deinit {
        debugPrint("[deinit] \(self)")
    }

    /// 构造方法
    override init(frame: CGRect) {
        super.init(frame: frame)
        configureView()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }

    //MARK: - 业务逻辑
    /// 开始播放
    /// - Parameter urlStr: 播放地址
    func play(_ urlStr: String?) {
        load(urlStr)
        autoPlay = true
    }

    func load(_ urlStr: String?) {
        guard let urlStr = urlStr else {return}
        guard urlStr != currentUrlStr else {
            activityView.startAnimating()
            return
        }
        guard let url = URL.init(string: urlStr) else {return}
        let item: AVPlayerItem = AVPlayerItem.init(url: url)
        defer {
            currentUrlStr = urlStr
            setPlayerData()
            addPlayerObserver()
            addPlayerItemObserver(item: item)
            activityView.startAnimating()
        }
        guard let _player = player else {
            player = AVPlayer.init(playerItem: item)
            if let _layer = layer as? AVPlayerLayer {
                _layer.player = player
            }
            return
        }
        _player.replaceCurrentItem(with: item)
        autoPlay = false
    }

    /// 开始播放
    func play() {
        autoPlay = true
        activityView.stopAnimating()
        self.player?.play()
    }

    /// 暂停播放
    func pause() {
        self.player?.pause()
    }

    /// 停止播放
    func stop() {
        player?.pause()
        player?.replaceCurrentItem(with: nil)
        currentItem = nil
        currentUrlStr = nil
    }

    //MARK: - UI
    private func configureView() {
        // 设置layer
        if let playLayer = layer as? AVPlayerLayer {
            playLayer.videoGravity = .resizeAspect
        }
        layer.backgroundColor = UIColor.black.cgColor

        let map: [String: UIView] = [
            "activity": activityView
        ]
        map.addTo(self)
        withVFL("H:[activity(40)]", views: map)
        withVFL("V:[activity(40)]", views: map)
        activityView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
        activityView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
        bringSubviewToFront(activityView)
        activityView.startAnimating()
    }
 }

//MARK: - 播放器相关配置和回调
extension CWPlayerView {
    /// 播放器初始化
    fileprivate func setPlayerData() {
        player?.allowsExternalPlayback = false
        player?.automaticallyWaitsToMinimizeStalling = false
        player?.usesExternalPlaybackWhileExternalScreenIsActive = false
    }

    /// 移除播放器监听
    fileprivate func removePlayerObserver() {
        guard let value = playClockTimeObserver else {return}
        player?.removeTimeObserver(value)
    }

    /// 监听播放器
    fileprivate func addPlayerObserver() {
        removePlayerObserver()
        let time = CMTime.init(value: 1, timescale: 1)
        playClockTimeObserver = player?.addPeriodicTimeObserver(forInterval: time, queue: nil, using: { [weak self] (time) in
            guard let item = self?.currentItem else {return}
            let time = CMTimeGetSeconds(item.currentTime())
//            debugPrint("当前播放时间: \(time)")
        })
    }

    /// 监听播放源
    fileprivate func addPlayerItemObserver(item: AVPlayerItem?) {

        guard let item = item else {return}

        guard item != currentItem else {return}

        if let _current = currentItem {
            removePlayerItemObserver(item: _current)
        }

        currentItem = item

        // 状态
        item.addObserver(self,
                         forKeyPath:"status",
                         options: .new
                         ,context: nil)

        // 缓存是否够用
        item.addObserver(self,
                         forKeyPath:"playbackBufferEmpty",
                         options: .new,
                         context: nil)

        // 缓存充足了,可以播放了
        item.addObserver(self,
                forKeyPath:"playbackLikelyToKeepUp",
                    options:.new,
                   context:nil)

        // 缓存时间
        item.addObserver(self,
                forKeyPath:"loadedTimeRanges",
                options:.new,
                   context:nil)

        // 准备播放了
        item.addObserver(self,
                         forKeyPath:"readyToPlay",
                         options: .new,
                         context: nil)

        // 播放完成之后的通知
        NotificationCenter.default.addObserver(self, selector: #selector(didFinishedPlay(_:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: item)

        // 视频播放被中断
        let session = AVAudioSession.sharedInstance()
        NotificationCenter.default.addObserver(self, selector: #selector(audioInterruption(_:)), name: AVAudioSession.interruptionNotification, object: session)
    }

    /// 移除播放源监听
    fileprivate func removePlayerItemObserver(item: AVPlayerItem?) {
        guard let item = item else {return}
        do {
            item.removeObserver(self, forKeyPath: "status")
            item.removeObserver(self, forKeyPath: "playbackBufferEmpty")
            item.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp")
            item.removeObserver(self, forKeyPath: "loadedTimeRanges")
            item.removeObserver(self, forKeyPath: "readyToPlay")
            NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: item)
            NotificationCenter.default.removeObserver(self, name: AVAudioSession.interruptionNotification , object: AVAudioSession.sharedInstance())
        }
    }

    /// 播放器状态发生改变逻辑
    fileprivate func playStatusChanged() {
        guard let player = player,
              let item = player.currentItem else {return}
        switch item.status {
        case .unknown:()
            debugPrint("status: unkonwn")
        case .failed:()
            debugPrint("status: failed")
        case .readyToPlay:
            debugPrint("status: readyToPlay")
            if autoPlay {
                play()
            }
        @unknown default:
            debugPrint("status: unknown default")
        }
    }

    /// 播放器状态监听
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        guard let keyPath = keyPath else {return}
        if "status".elementsEqual(keyPath) {
            playStatusChanged()
        }else if "playbackBufferEmpty".elementsEqual(keyPath) {
            // 缓冲不够
            debugPrint("缓存不够用了啊啊啊!")
            activityView.startAnimating()
        }else if "playbackLikelyToKeepUp".elementsEqual(keyPath) {
            // 缓冲够了
            if isPlaying{
                activityView.stopAnimating()
                player?.play()
            }
        }else if "loadedTimeRanges".elementsEqual(keyPath) {
            // 当前缓冲进度
            guard let arr = currentItem?.loadedTimeRanges else {return}
            guard false == arr.isEmpty else {return}
            guard let timeRange = arr[0] as? CMTimeRange else {return}
            let start = CMTimeGetSeconds(timeRange.start)
            let duration = CMTimeGetSeconds(timeRange.duration)
            let total = start + duration
            debugPrint("\(self)缓冲时长：\(total)")
        }else {
            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        }
    }

    /// 播放完毕回调
    @objc fileprivate func didFinishedPlay(_ sender: Notification) {
        debugPrint("播放完毕...")
        currentItem?.cancelPendingSeeks()
        currentItem?.seek(to: CMTime.zero, completionHandler: { [weak self] finish in
            guard finish else {return}
            self?.player?.play()
        })
    }

    /// 播放中断回调
    @objc fileprivate func audioInterruption(_ sender: NSNotification) {
        debugPrint("播放中断...")
    }
}
